package com.xinmao.quantitative.trad.strategies.grid;

import com.xinmao.quantitative.FundTrade;
import com.xinmao.quantitative.enums.GridTansType;
import com.xinmao.quantitative.model.GridModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.ta4j.core.Bar;
import org.ta4j.core.BarSeries;
import org.ta4j.core.Trade;
import org.ta4j.core.analysis.cost.CostModel;
import org.ta4j.core.num.DecimalNum;
import org.ta4j.core.num.Num;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Service
public class GridTradingStrategy {

    public List<Trade> executeStrategy(BarSeries series, GridModel gridModel,LinkedList<Num> gridLines) {
        List<Trade> trades = new ArrayList<>();
        // 计算开始价格,从区间中线进入网格,初始化上一网格位置
        int previousGrid = gridLines.size()/2 + 1;
        //最大持仓数量
        if(gridModel.getMaxHoldNum() == null || gridModel.getMaxHoldNum().isLessThanOrEqual(DecimalNum.valueOf(0))){
            gridModel.setMaxHoldNum(gridModel.getBuyNum().multipliedBy(DecimalNum.valueOf(gridLines.size() - previousGrid)));
        }
        for (int i = 0; i < series.getBarCount(); i++) {
            Bar currentBar = series.getBar(i);
            Num currentPrice = currentBar.getClosePrice();
            for (int j = 0; j < gridLines.size(); j++) {
                //确定当前价格所在网格位置
                if (currentPrice.isGreaterThan(gridLines.get(j)) && currentPrice.isLessThanOrEqual(gridLines.get(j - 1))) {
                    //获取当前持仓数量
                    Num bilges =  getBilges(trades);
                    if (previousGrid < j && isInGrid(gridModel, gridLines, currentPrice,previousGrid) && bilges.isLessThanOrEqual(gridModel.getMaxHoldNum())) {
                        //满足跌幅开仓
                        Trade openPosition = openPosition(currentBar, previousGrid, j, gridModel.getCostModel(), gridModel.getBuyNum());
                        if(bilges.plus(openPosition.getAmount()).isGreaterThan(gridModel.getMaxHoldNum())){
                            openPosition(currentBar, previousGrid + 1, j, gridModel.getCostModel(), gridModel.getMaxHoldNum());
                            trades.add(openPosition);
                            previousGrid = j;
                        }else {
                            trades.add(openPosition);
                            previousGrid = j;
                        }
                    }
                    bilges =  getBilges(trades);
                    if (previousGrid > j && isInGrid(gridModel, gridLines, currentPrice,previousGrid) && bilges.isGreaterThan(DecimalNum.valueOf(0))) {
                        Trade closePosition = closePosition(currentBar,  previousGrid, j, gridModel.getCostModel(), gridModel.getBuyNum());
                        if(closePosition.getAmount().isGreaterThanOrEqual(bilges)){
                            closePosition = closePosition(currentBar, j + 1, j, gridModel.getCostModel(), bilges);
                            trades.add(closePosition);
                            previousGrid = j;
                        }else {
                            trades.add(closePosition);
                            previousGrid = j;
                        }
                    }
                    break;
                }
            }
            if (currentPrice.isGreaterThan(gridLines.getFirst()) || currentPrice.isLessThan(gridLines.getLast())) {
                log.info("价格：{}，走出价格区间：{}--{}", currentPrice, gridLines.getFirst(), gridLines.getLast());
            }

        }
        return trades;
    }

    private Num getBilges(List<Trade> trades){
        Num holdBuyNum = trades.stream().filter(Objects::nonNull)
                .filter(trade -> trade.getType().equals(Trade.TradeType.BUY))
                .map(Trade::getAmount).reduce(DecimalNum.valueOf(0), Num::plus);
        Num holdSellNum = trades.stream().filter(Objects::nonNull)
                .filter(trade -> trade.getType().equals(Trade.TradeType.SELL))
                .map(Trade::getAmount).reduce(DecimalNum.valueOf(0), Num::plus);
        return holdBuyNum.minus(holdSellNum);
    }


    private boolean isInGrid(GridModel gridModel,LinkedList<Num> gridLines, Num currentPrice, int previousGrid) {
        if(gridModel.getGridTansType().equals(GridTansType.PERCENT)) {
            Num gridPercent = gridLines.get(previousGrid).minus(currentPrice).dividedBy(gridLines.get(previousGrid));
//            log.info("当前价格：{}，上一网格价格差%：{}",currentPrice,gridPercent);
            if(gridPercent.abs().isGreaterThan(gridModel.getByPercent())){
                return true;
            }
        }
        if(gridModel.getGridTansType().equals(GridTansType.PRICE)){
            Num gridPrice = gridLines.get(previousGrid).minus(currentPrice);
//            log.info("当前价格：{}，上一网格价格差：{}",currentPrice,gridPrice);
            return gridPrice.abs().isGreaterThan(gridLines.get(previousGrid).minus(gridLines.get(previousGrid - 1)));
        }
        return false;
    }

    /**
     * 按固定数量生成网格
     * */
    public LinkedList<Num> generateGridByNum(Num lowestPrice, Num highestPrice, int n) {
        //目标价格最小单位
        int scale = new BigDecimal(highestPrice.toString()).scale();
        Num minPrice = DecimalNum.valueOf(Math.pow(10, -scale));

        // 计算价格间隔
        Num gap = highestPrice.dividedBy(lowestPrice).pow(DecimalNum.valueOf(1).dividedBy(DecimalNum.valueOf(n)))
                .minus(DecimalNum.valueOf(1));

        if (gap.multipliedBy(lowestPrice).isLessThan(minPrice)) {
            throw new RuntimeException("最小网格价格区间小于最小交易金额单位,价格单位样例：" + lowestPrice + ",最小网格单位：" + gap);
        }
        LinkedList<Num> price = new LinkedList<>();
        for (int i = 0; i <= n; i++) {
            Num calculatedPrice = DecimalNum.valueOf(1).plus(gap).pow(DecimalNum.valueOf(i)).multipliedBy(lowestPrice);
            price.add(DecimalNum.valueOf(calculatedPrice.toString(), scale));
        }
        Collections.reverse(price);
        return price; // 返回网格列表
    }

    /**
     * 按每格百分比自动生成网格
     * */
    public LinkedList<Num> generateGridByPercent(Num lowestPrice, Num highestPrice, Num percent) {
        //目标价格最小单位
        int scale = new BigDecimal(highestPrice.toString()).scale();
        Num minPrice = DecimalNum.valueOf(Math.pow(10, -scale));
        // 计算价格间隔
        Num gridPrice = lowestPrice.multipliedBy(percent);
        if (gridPrice.isLessThan(minPrice)) {
            throw new RuntimeException("最小网格价格区间小于最小交易金额单位,价格单位样例：" + lowestPrice + ",最小网格单位：" + gridPrice);
        }
        LinkedList<Num> price = new LinkedList<>();

        for (int i = 0; i < Integer.MAX_VALUE ; i++){
            Num calculatedPrice = DecimalNum.valueOf(1).plus(percent).pow(DecimalNum.valueOf(i)).multipliedBy(lowestPrice);
            calculatedPrice = DecimalNum.valueOf(calculatedPrice.toString(), scale);
            if(calculatedPrice.isGreaterThanOrEqual(highestPrice) && calculatedPrice.minus(highestPrice).isGreaterThanOrEqual(minPrice)){
                price.add(highestPrice);
                break;
            }else if(calculatedPrice.minus(highestPrice).equals(DecimalNum.valueOf(0))){
                price.add(highestPrice);
                break;
            }
            price.add(calculatedPrice);
        }
        Collections.reverse(price);
        return price; // 返回网格列表
    }

    /**
     * 开仓逻辑
     */
    private Trade openPosition(Bar bar, int previousGrid, int index, CostModel costModel, Num copies) {
        Num hold = copies.multipliedBy(DecimalNum.valueOf(Math.abs(index - previousGrid)));
        return new FundTrade(index, Trade.TradeType.BUY, bar.getClosePrice(), hold, costModel);
    }

    /**
     * 平仓逻辑
     */
    private Trade closePosition(Bar bar, int previousGrid, int index, CostModel costModel, Num copies) {
        Num hold = copies.multipliedBy(DecimalNum.valueOf(Math.abs(index - previousGrid)));
        return new FundTrade(index, Trade.TradeType.SELL, bar.getClosePrice(), hold, costModel);
    }

}